package sk.avicularia.core.dao;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;

import sk.avicularia.common.Args;
import sk.avicularia.core.exception.NoEntityExistIdException;
import sk.avicularia.core.exception.NonUniqueEntityIdException;

/**
 * Zakladny obkekt pre pristup k DB. 
 * 
 * @author leaflock
 * @since  0.1
 */
public class DAO
{
	/**
	 * Meno JPA perzistentne jednotky kam sa budu ukladat data
	 */
	public static final String PERSISTENCE_UNIT_NAME = "JpaPU";
	
	/**
	 * Id, ktore nebude v DB nikdy existovat
	 */
	public static final Long DUMMY_ID = Long.valueOf(-1);
	
	/**
	 * JPA pre pripojenie na perzistentne uloziste, jeho meno je {@link #PERSISTENCE_UNIT_NAME}
	 */
	@PersistenceContext(unitName = PERSISTENCE_UNIT_NAME)
	private EntityManager em; 
	
	/**
	 * Nerobi nic
	 */
	public DAO() {}
		
	/**
	 * Vracia EntityManager na pristup k DB
	 * @return
	 */
	public EntityManager getEm()
	{
		return em;
	}

	/**
	 * Vrati prvu entity z vysledneho dotazu. Vsetky atributy dotazu spolu 
	 * s {@link Query#setMaxResults(int)} uz musia byt nastavene
	 * 
	 * @param query dotaz pre najadenie entity
	 */
	public Object findFirstEntity(final Query query)
	{
		@SuppressWarnings("unchecked")
		List<Object> entity = query.getResultList();
		if (entity.size() >= 1)
		{
			return entity.get(0);
		}
		else
		{
			return null;
		}
	}
	
	/**
	 * Vrati prvu entity z vysledneho dotazu. Vsetky atributy dotazu spolu 
	 * s {@link Query#setMaxResults(int)} uz musia byt nastavene
	 * 
	 * @param query dotaz pre najadenie entity
	 */
	public <T> T findFirstEntity(final TypedQuery<T> query)
	{
		@SuppressWarnings("unchecked")
		T entity = (T) findFirstEntity((Query) query);
		return entity;
	}
	
	/**
	 * Najdenie entity v perzistentnom ulozisti
	 * 
	 * @param entityClass je trieda hladanej entity
	 * @param primaryKey je identifikacia hladanej entity
	 * @return hladana entita, ak nenajde zaznam vrati null
	 * @see EntityManager#find(Class, Object)
	 */
	public <T> T findEntity(Class<T> entityClass, Object primaryKey)
	{
		T result = this.getEm().find(entityClass, primaryKey);
		return result;
	}
	
	/**
	 * Najdenie entity v perzistentnom ulozisti
	 * 
	 * @param entityClass je trieda hladanej entity
	 * @param primaryKey je identifikacia hladanej entity
	 * @return hladana entita, nikdy nevracia null
	 * @throws
	 * @see EntityManager#find(Class, Object)
	 */
	public <T> T getEntity(Class<T> entityClass, Object primaryKey)
	{
		T result = findEntity(entityClass, primaryKey);
		
		if (result == null)
		{
			throw new NoEntityExistIdException("Entity not exist. " + new Args("entityClass", entityClass, "primaryKey", primaryKey)); 
		}
		return result;
	}
	
	/**
	 * Metoda pre ziskanie entity. Entita je definovana dotazom.  
	 * Vsetky atributy dotazu spolu s {@link Query#setMaxResults(int)} su nastavene.
	 * Entita je ziskana volanim {@link Query#getSingleResult()}
	 * 
	 * @param query dotaz ktory definuje prave jednu entitu
	 * @return entita ktoru definuje dotaz
	 * @throws NoEntityExistIdException ak entita neexistuje
	 * @throws NonU
	 */
	public Object getFirstEntity(Query query)
	{
		try 
		{
			Object result = query.getSingleResult();
			return result;
		}
		catch (NoResultException e)
		{
			throw new NoEntityExistIdException(e);
		}
		catch (NonUniqueResultException e)
		{
			throw new NonUniqueEntityIdException(e);
		}
	}
	
	
}
